Windows Live Ag... 的个人资料Windows Live Agents照片日志列表 工具 帮助

日志


9月25日

Creating And Adding Dynamic Display Pictures (DDP) to Windows Live Agents

Dynamic Display Pictures are animated display pictures with advanced functionality (Moods, Animation) that users can create and purchase from third party partners.  Due to security concerns, Dynamic Display Pictures can only be purchased from official Microsoft content partners such as:

American Greetings/Blue Mountain à http://www.aginteractive.com/products_overview.html

3H Group (MeeGos) à http://www.3hgroup.com/index.htm

Saw-You (Weemees) à http://www.weeworld.com/

Wisepost/YNK (Korea) à http://www.ynkkorea.com/

DDPs support Flash (.swf) files for animations and conversation-specific moods. You must also include a flat image (96 x 96 pixels, gif or png) for the default image to be used for downlevel applications, including Mac Messenger, mobile devices, etc. The default size and shape of a dynamic display file is 142 x 98 pixels. Since a dynamic display picture consists of multiple elements it needs to be shipped as a .CAB file. This .CAB file is cryptographically signed and placed along with the signature in a content pack (e.g. a theme). It will be shipped as a .CAB file inside a .CAB file. Sometimes .CAB files are also .MCT files. Despite the different extension, the contents are identical.

When developing a Windows Live Agent that uses a Dynamic Display Picture, use the following criteria:

 

                - An *uncompressed* .CAB or .MCT file with the appropriate assets

                - ASCII content.xml files. Unicode is not currently supported.

                - A Microsoft signature -- this may be inside the .CAB or .MCT, or it may be a separate file

 

Add the following line to the service in your .bfg that should display the DDP:

 

               <buddyicon>

            $_BFG_DIR/sparkle-pack.cab

      </buddyicon>

 

You can also use buddyicon-dynamic, but it's deprecated in current builds of the SDK. It can be usefull in case you have a compressed .CAB file. You decompress it manually and setup the .BFG file with the individual components inside it.               

 

               <buddyicon>

            $_BFG_DIR/sparkle-downlevel.png

      </buddyicon>

      <buddyicon-dynamic>

            $_BFG_DIR/sparkle-signed.cab

      </buddyicon-dynamic>

      <buddyicon-cert>

            $_BFG_DIR/sparkle.sig

      </buddyicon-cert>

      <buddyicon-name>

            Secret Sparkle

      </buddyicon-name> 

9月17日

How to add automated tests to your agent

You want to make sure that the most complex features of your project work and keep working throughout development. This post explains how to write tests and how to run them either one by one or via the "Sanity Check" mechanism.

 

 

Testing one feature

 

Let’s assume you have a feature, you give it a noun (singular or plural) and it returns the singular or plural form of the noun. For instance:

User: What is the plural of cat?

Automated Agent: The plural form of cat is cats.

Here is how you write a test for this feature: 

procedure TestForGrammarFeatures(CTX)

  if (!CTX.Summary)

    - *****Testing Grammar Features*****

  //

  // Nouns

  //

  RESULT = TestQuery(CTX, "What is the plural of cat?", "The plural form of cat is cats.")

  RESULT = TestQuery(CTX, "What is the plural of foot?", "The plural form of foot is feet.")

  RESULT = TestQuery(CTX, "What is the plural of child?", "The plural form of child is children.")

  RESULT = TestQuery(CTX, "What is the singular of cats?", "The singular form of cats is cat.")

  RESULT = TestQuery(CTX, "What is the singular of feet?", "The singular form of feet is foot.")

  RESULT = TestQuery(CTX, "What is the singular of children?", "The singular form of children is child.")

  //

  // ...this is where you add more test.

  //RESULT = TestQuery(CTX, "A user query", "A regular expression that the answer should match on")

 

+ _start_test_grammar_features [TEST_SUMMARY=Anything] {MACRO_PROTECTED_MATCHING}

  // CTX and TEST_SUMMARY are variables used internally by the testing engine

  CTX = InitializeTest(TEST_SUMMARY, "Grammar Test")

  call TestForGrammarFeatures(CTX)

  call ReportTestResults(CTX, "Grammar Test")

 

Once you execute the test by calling "_start_test_grammar_features", it will run every single query in it and compare the answers your agent gave against the expected results (which are regular expressions). Ideally the result would look like this: 

User: _start_test_grammar_features

Automated Agent: Done testing Grammar Test (6 tests): Grammar Test passed 

For every query in the test that might have failed, the expected and the returned result are displayed. Tip: If you are interested in writing tests with more complex regular expressions you can look in BuddyScriptLib\English\Core\DateTime.pkg. There are a lot of tests at the bottom of the file covering all the different date and time features.

Every complex feature should have its own tests to assure its functionality.

 

 

Testing your Agent

 

A simple but very efficient way to check that all of your tests are working properly is the "Sanity Check". All you have to do is register a test to the Sanity Check with the help of function RegisterTestQuery("name_of_test_here"). 

procedure ABStartSessionProc()

  call RegisterTestQuery("_start_test_grammar_features")

  //

  // ...this is where you register more tests.

  //

The procedure ABStartSessionProc() is called whenever a user session starts. In a single procedure ABStartSessionProc() you can add as many tests to register as you want to and you can use ABStartSessionProc() more than once in different domains/packages as well. Ideally you should register a test to the Sanity Check in the same .ddl (Domain Description File) from where you call the test, just to keep it organized.

To launch the Sanity Check, simply type "project_sanity" as a user query. Once you have tests registered, we recommend to use the Sanity Check whenever you publish your project, when you make significant changes to your project, or even in automated tests.

 Note: The Sanity Check is available in KnowledgeManagement mode only (in the IDE, add "--filter KnowledgeManagement" to the field "Connection Options -> Extra Options"). 

 
9月10日

Using StringToXML and XMLToString

 

One of the new BuddyScript functions introduced in the 4.2 release of the SDK is StringToXML. The goal of this blog post is to familiarize you with how to use it. XMLToString does the reverse operation and will not be discussed in detail here, but it should be intuitive to use once you have an understanding of StringToXML.

Let’s assume that you have XML file that needs parsing. In the simple case of a hierarchical structure, we could use simple xml filter, but for a more complex case you may find StringToXML to be more useful. For example, you could have some movie information as follows:

<movies>

 <city name="London"/>

 <info>

   <title>Gone With The Wind</title>

 </info>

 <city name="Manchester" />

 <info>

   <title>Pirates of the Carribean</title>

 </info>

</movies>

 

In this case it’s not entirely clear how one would easily use the simple XML datasource for parsing. You could try to parse this function with regular expression matching, however one such XML feed may be more easily (and less error prone) parsed using the StringToXML function.

StringToXML takes as a parameter a string of the XML source and will return an XML object that can be more easily traversed.

In this example, the XML object returned from this XML by StringToXML would be as follows (generated using dbg_display XML_OBJECT)

!-kind = ""

!-name = "movies"

!-nodes

! !-0

! ! !-kind = ""

! ! !-name = "city"

! ! !-attributes

! ! ! !-name = "London"

! !-1

! ! !-kind = ""

! ! !-name = "info"

! ! !-nodes

! ! ! !-0

! ! ! ! !-kind = ""

! ! ! ! !-name = "title"

! ! ! ! !-nodes

! ! ! ! ! !-0

! ! ! ! ! ! !-kind = 2 (int)

! ! ! ! ! ! !-content = "Gone With The Wind"

! !-2

! ! !-kind = ""

! ! !-name = "city"

! ! !-attributes

! ! ! !-name = "Manchester"

! !-3

! ! !-kind = ""

! ! !-name = "info"

! ! !-nodes

! ! ! !-0

! ! ! ! !-kind = ""

! ! ! ! !-name = "title"

! ! ! ! !-nodes

! ! ! ! ! !-0

! ! ! ! ! ! !-kind = 2 (int)

! ! ! ! ! ! !-content = "Pirates of the Carribean"

 

To decipher the object, the following are various values that the members can take on.

Name

Possible Values

Explanation

kind

1, 2**

1: this is either an attribute or more nodes will follow

2: this XML node has content

name

String value

The name of the XML element

nodes

List of nodes

Contains a list of nodes as specified in this table

attributes

Name value pairs of attributes

Names and values of each attribute will be placed in this list

content

Actual content

Actual content of the XML node

 

** There is a small bug in 4.2 where “kind” is either “” or 2. In 4.3, kind will either be 1 or 2. For 4.2, just detect for “2” or not “2”.

Here is a full example of how to navigate the above XML file using StringToXML.

datasource dsMovieInfo1(URL) => RESULT {timeout = "20" expire = "now"}

  http

    URL

 

//returns the data in a list

function GetMovieInfo1()

  ALL_MOVIE_INFO = ()

  MOVIE_INFO.CITY = ""

  MOVIE_INFO.TITLE = ""

  //the XML file in this example is being served by my local machine

  T_INFO = dsMovieInfo1("http://localhost/gadgets/movietest.xml")

  XML_OBJECT = StringToXML(T_INFO)

  //nice display for XML object, very useful for debugging purposes

  dbg_display XML_OBJECT

  for value GENERAL_INFO in XML_OBJECT.nodes

    if GENERAL_INFO.name eq "city" && Exist(GENERAL_INFO.attributes) && Exist(GENERAL_INFO.attributes.name)

      MOVIE_INFO.CITY = GENERAL_INFO.attributes.name

    if GENERAL_INFO.name eq "info" && Exist(GENERAL_INFO.nodes)

      for value MOVIE_DETAILS in GENERAL_INFO.nodes

        if MOVIE_DETAILS.name eq "title" && Exist(MOVIE_DETAILS.nodes)

          MOVIE_INFO.TITLE = MOVIE_DETAILS.nodes[0].content

          //keep a list of the results

          insert last in ALL_MOVIE_INFO MOVIE_INFO

          //reset movie info

          MOVIE_INFO.CITY = ""

          MOVIE_INFO.TITLE = ""

  return ALL_MOVIE_INFO

 

? Test StringToXML

  MOVIEINFO = GetMovieInfo1()

  - MOVIEINFO

 

Note: In this release StringToXML only works for ANSI encoded sources. In future releases we are planning to add support for Unicode, UTF-8 and other encodings.

Please feel free to comment and ask questions on this post (or others) at our forum found at:

http://forums.microsoft.com/MSDN/ShowForum.aspx?ForumID=1687&SiteID=1

Other suggestions for what tutorials or topics you would like to see are also very welcome.